package fr.inria.arles.temperatureManagement;

import java.net.InetSocketAddress;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;

import static fr.inria.arles.temperatureManagement.Constants.NETWORK_TIMEOUT_MILLIS;
import static fr.inria.arles.temperatureManagement.Utility.*;

public class ProcessingNodeCommunicator {

	/**
	 * Updates the current reference temperature setting by invoking the 
	 * processing node
	 * 
	 * @param temp
	 *            the reference temperature
	 * @param processingNodeIP
	 *            The IP Address of the Processing Node Server
	 * @param processingNodePort
	 *            The port number of the Processing Node Server
	 * @param partition
	 *            The partition being referred to
	 * @throws ProcessingNodeConnectionFailureException
	 *             if there is an error in communicating with the Processing
	 *             Node Server
	 * @throws UnknownHostException
	 *             if the Processing Node's IP can not be resolved.
	 * @throws IOException
	 *             if an I/O exception has occurred while communicating over the
	 *             network.
	 */
	public static void updateTemp(String processingNodeIP,
			int processingNodePort, String partition, String temp)
			throws IOException, ProcessingNodeConnectionFailureException {
		System.out.println("Connecting to Processing Node Server :- "
				+ processingNodeIP + ":" + processingNodePort
				+ " to update temperature setting");
		Socket s = getSocket(processingNodeIP, processingNodePort,
				NETWORK_TIMEOUT_MILLIS);
		OutputStream outStream = s.getOutputStream();
		InputStream inStream = s.getInputStream();

		PrintWriter out = new PrintWriter(outStream, true);
		String req = Utility.encode("UpdateTemp", partition, temp);
		out.println(req);

		Scanner in = new Scanner(inStream);
		String res = in.nextLine();
		s.close();
		if (!res.equals("true")) {
			throw new ProcessingNodeConnectionFailureException();
		}

	}

	/**
	 * Gets the current actual temperature setting from the processing node.
	 * 
	 * @param processingNodeIP
	 *            The IP Address of the Processing Node Server
	 * @param processingNodePort
	 *            The port number of the Processing Node Server
	 * @param partition
	 *            The partition being referred to
	 * @return the Reference Temperature
	 * @throws ProcessingNodeConnectionFailureException
	 *             if there is an error in communicating with the Processing
	 *             Node Server
	 * @throws UnknownHostException
	 *             if the Processing Node's IP can not be resolved.
	 * @throws IOException
	 *             if an I/O exception has occurred while communicating over the
	 *             network.
	 */
	public static String getTemp(String processingNodeIP,
			int processingNodePort, String partition) throws IOException,
			ProcessingNodeConnectionFailureException {
		System.out.println("Connecting to Processing Node Server :- "
				+ processingNodeIP + ":" + processingNodePort
				+ " to get actual temperature.");
		Socket s = getSocket(processingNodeIP, processingNodePort,
				NETWORK_TIMEOUT_MILLIS);
		OutputStream outStream = s.getOutputStream();
		InputStream inStream = s.getInputStream();

		PrintWriter out = new PrintWriter(outStream, true);
		String req = Utility.encode("GetTemp", partition);
		out.println(req);

		Scanner in = new Scanner(inStream);
		String res = in.nextLine();
		s.close();
		String[] response = decode(res);
		if (response.length == 2 && response[0].equals("true")) {
			return response[1];
		} else {
			throw new ProcessingNodeConnectionFailureException();
		}

	}
        
        private static Socket getSocket(String IP, int port, int timeoutMillis)
			throws IOException {
		InetSocketAddress addr = new InetSocketAddress(IP, port);
		Socket s = new Socket();
		s.setSoTimeout(timeoutMillis);
		s.connect(addr, timeoutMillis);
		return s;
	}

}
